﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

using ERPStore.Extensions;

using CloudSoft.Extensions;
using System.Threading.Tasks;

namespace ERPStore.Services
{
	[Obsolete("Use EventBrokR instead", true)]
	public class EventPublisher : IEventPublisher
	{
		private Queue<Action> m_Queue = new Queue<Action>();
		private ManualResetEvent m_NewEvent = new ManualResetEvent(false);
		private ManualResetEvent m_Terminate = new ManualResetEvent(false);
		private Thread m_Thread;
		private bool m_Terminated = false;

		private static object m_Lock = new object();

		public EventPublisher(IEventSubscriptionService subscriptionService)
		{
			this.SubscriptionService = subscriptionService;

			m_Thread = new Thread(new ThreadStart(ProcessQueue));
			m_Thread.Name = "EventPublisher";
			m_Thread.IsBackground = true;
			m_Thread.Start();
		}

		protected IEventSubscriptionService SubscriptionService { get; private set; }
		protected Logging.ILogger Logger 
		{
			get
			{
				return GlobalConfiguration.Configuration.DependencyResolver.GetService<ERPStore.Logging.ILogger>();
			}
		}

		public void Stop()
		{
			m_Terminated = true;
			m_Terminate.Set();
			m_Queue.Clear();
			if (m_Thread != null && !m_Thread.Join(TimeSpan.FromSeconds(2)))
			{
				m_Thread.Abort();
			}
		}

		public Task PublishAsync<T>(T eventMessage, int delay = 0)
		{
			return Task.Run(() =>
				{
					if (delay > 0)
					{
						Task.Delay(delay).RunSynchronously();
					}
					Publish(eventMessage);
				});
		}

		/*
		internal void Publish<T>(T eventMessage)
		{
			if (m_Terminated)
			{
				return;
			}
			// async disabled for tests
			if (GlobalConfiguration.Configuration.ExecutingContext == Models.ExecutingContext.Test)
			{
				Publish(eventMessage);
				return;
			}
			lock (m_Queue)
			{
				m_Queue.Enqueue(() => Publish(eventMessage));
			}
			m_NewEvent.Set();
		}*/

		private void Publish<T>(T eventMessage)
		{
			Logger.Debug("Publish eventMessage : {0}", eventMessage.GetType().FullName);
			var subscriptions = SubscriptionService.GetSubscriptions<T>();
			if (subscriptions.IsNullOrEmpty())
			{
				Logger.Debug("No subscriber found for event message");
				return;
			}

			foreach (var subscription in subscriptions)
			{
				Logger.Debug("Publish event message to subscriber : {0}", subscription.GetType().FullName);
				PublishToConsumer(subscription, eventMessage);
			}
		}

		private void PublishToConsumer<T>(IConsumer<T> x, T eventMessage)
		{
			try
			{
				x.Handle(eventMessage);
				Logger.Debug("eventMessage sent to : {0}", x.GetType().FullName);
			}
			catch (Exception ex)
			{
				Logger.Error(ex);
			}
			finally
			{
				var instance = x as IDisposable;
				if (instance != null)
				{
					instance.Dispose();
				}
			}
		}

		void ProcessQueue()
		{
			while (!m_Terminated)
			{
				var waitHandles = new WaitHandle[] { m_Terminate, m_NewEvent };
				int result = ManualResetEvent.WaitAny(waitHandles, 60 * 1000, true);
				if (result == 0)
				{
					m_Terminated = true;
					break;
				}
				m_NewEvent.Reset();

				if (m_Queue.Count == 0)
				{
					continue;
				}
				// Enqueue
				Queue<Action> queueCopy;
				lock (m_Queue)
				{
					queueCopy = new Queue<Action>(m_Queue);
					m_Queue.Clear();
				}

				foreach (var process in queueCopy)
				{
					process();
				}
			}
		}

	}
}
